#ifndef NULL_RENDER_AUX_GEOM_H
#define NULL_RENDER_AUX_GEOM_H

#include "../Common/RenderAuxGeom.h"

class CNULLRenderer;
class ICrySizer;

#if ( defined(WIN32) || defined(WIN64) ) && !defined(_RELEASE)
#define ENABLE_WGL_DEBUG_RENDERER
#endif

#ifdef ENABLE_WGL_DEBUG_RENDERER
#include <windows.h>
#include <gl/GL.h>
#include <gl/GLU.h>
#endif

class CNULLRenderAuxGeom : public IRenderAuxGeom
{
public:
	// interface
	virtual void SetRenderFlags( const SAuxGeomRenderFlags& renderFlags ) {}
	virtual SAuxGeomRenderFlags GetRenderFlags() { return SAuxGeomRenderFlags(); }

	virtual void Flush() {}

	virtual void DrawPoint( const Vec3& v, const ColorB& col, uint8 size = 1 );
	virtual void DrawPoints( const Vec3* v, uint32 numPoints, const ColorB& col, uint8 size = 1 );
	virtual void DrawPoints( const Vec3* v, uint32 numPoints, const ColorB* col, uint8 size = 1 );

	virtual void DrawLine( const Vec3& v0, const ColorB& colV0, const Vec3& v1, const ColorB& colV1, float thickness = 1.0f );
	virtual void DrawLines( const Vec3* v, uint32 numPoints, const ColorB& col, float thickness = 1.0f );
	virtual void DrawLines( const Vec3* v, uint32 numPoints, const ColorB* col, float thickness = 1.0f );
	virtual void DrawLines( const Vec3* v, uint32 numPoints, const uint16* ind, uint32 numIndices, const ColorB& col, float thickness = 1.0f );
	virtual void DrawLines( const Vec3* v, uint32 numPoints, const uint16* ind, uint32 numIndices, const ColorB* col, float thickness = 1.0f );
	virtual void DrawPolyline( const Vec3* v, uint32 numPoints, bool closed, const ColorB& col, float thickness = 1.0f );
	virtual void DrawPolyline( const Vec3* v, uint32 numPoints, bool closed, const ColorB* col, float thickness = 1.0f );

	virtual void DrawTriangle( const Vec3& v0, const ColorB& colV0, const Vec3& v1, const ColorB& colV1, const Vec3& v2, const ColorB& colV2 );
	virtual void DrawTriangles( const Vec3* v, uint32 numPoints, const ColorB& col );
	virtual void DrawTriangles( const Vec3* v, uint32 numPoints, const ColorB* col );
	virtual void DrawTriangles( const Vec3* v, uint32 numPoints, const uint16* ind, uint32 numIndices, const ColorB& col );
	virtual void DrawTriangles( const Vec3* v, uint32 numPoints, const uint16* ind, uint32 numIndices, const ColorB* col );

	virtual void DrawAABB( const AABB& aabb, bool bSolid, const ColorB& col, const EBoundingBoxDrawStyle& bbDrawStyle ) {}
	virtual void DrawAABBs( const AABB* aabbs, uint32 aabbCount, bool bSolid, const ColorB& col, const EBoundingBoxDrawStyle& bbDrawStyle ) {}
	virtual void DrawAABB( const AABB& aabb, const Matrix34& matWorld, bool bSolid, const ColorB& col, const EBoundingBoxDrawStyle& bbDrawStyle ) {}

	virtual void DrawOBB( const OBB& obb, const Vec3& pos, bool bSolid, const ColorB& col, const EBoundingBoxDrawStyle& bbDrawStyle ) {}
	virtual void DrawOBB( const OBB& obb, const Matrix34& matWorld, bool bSolid, const ColorB& col, const EBoundingBoxDrawStyle& bbDrawStyle ) {}

	virtual void DrawSphere( const Vec3& pos, float radius, const ColorB& col, bool drawShaded = true );
	virtual void DrawCone( const Vec3& pos, const Vec3& dir, float radius, float height, const ColorB& col, bool drawShaded = true ) {}
	virtual void DrawCylinder( const Vec3& pos, const Vec3& dir, float radius, float height, const ColorB& col, bool drawShaded = true ) {}

	virtual void DrawBone( const QuatT& rParent, const QuatT& rBone, ColorB col ) {}
	virtual void DrawBone(  const Matrix34& rParent, const Matrix34& rBone, ColorB col ) {}
	
public:
	static CNULLRenderAuxGeom* Create( CNULLRenderer& renderer )
	{
		if (s_pThis == NULL)
			s_pThis = new CNULLRenderAuxGeom( renderer );
		return s_pThis;
	}

public:
	~CNULLRenderAuxGeom();

	void BeginFrame();
	void EndFrame();

private:
	CNULLRenderAuxGeom( CNULLRenderer& renderer );

  CNULLRenderer *m_renderer;

	static CNULLRenderAuxGeom* s_pThis;

#ifdef ENABLE_WGL_DEBUG_RENDERER
	static void DebugRendererShowWindow(IConsoleCmdArgs* args)
	{
		if (!s_pThis)
			return;

		ShowWindow(s_pThis->m_hwnd, s_hidden ? SW_SHOWNA : SW_HIDE);
		s_hidden = !s_hidden;
	}

	static void DebugRendererSetEyePos(IConsoleCmdArgs* args)
	{
		if (!s_pThis)
			return;

		if (args->GetArgCount() != 4)
			return;

		Vec3 pos;
		pos.x = (float)atof(args->GetArg(1));
		pos.y = (float)atof(args->GetArg(2));
		pos.z = (float)atof(args->GetArg(3));

		s_pThis->m_eye = pos;
	}

	HWND m_hwnd;
	HDC m_hdc;
	HGLRC m_glrc;

	GLUquadricObj* m_qobj;

	// Window Procedure
	static LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
	{
		switch (message)
		{
		case WM_CREATE:
			return 0;

		case WM_CLOSE:
			PostQuitMessage(0);
			return 0;

		case WM_DESTROY:
			return 0;

		case WM_ACTIVATE:
			if ( LOWORD(wParam) != WA_INACTIVE )
				s_active = true;
			else
				s_active = false;
			return 0;

		default:
			return DefWindowProc( hWnd, message, wParam, lParam );
		}
	}

	void EnableOpenGL();
	void DisableOpenGL();

	struct SPoint // GL_C3F_V3F
	{
		GLfloat color[3]; // rgb
		GLfloat vertex[3]; // xyz

		SPoint() {}

		SPoint(const Vec3& v, const ColorB& c)
		{
			color[0] = c.r/255.f; color[1] = c.g/255.f; color[2] = c.b/255.f;
			vertex[0] = v.x; vertex[1] = v.y; vertex[2] = v.z;
		}

		const SPoint& operator=(const SPoint& rhs)
		{
			memcpy(color, rhs.color, sizeof(color));
			memcpy(vertex, rhs.vertex, sizeof(vertex));
			return *this;
		}
	};

	std::vector<SPoint> m_points;

	struct SLine
	{
		SPoint points[2];

		SLine() {}
		SLine(const SPoint& p0, const SPoint& p1)
		{
			points[0] = p0;
			points[1] = p1;
		}
	};

	std::vector<SLine> m_lines;

	struct SPolyLine
	{
		std::vector<SPoint> points;
	};

	std::vector<SPolyLine> m_polyLines;

	struct STriangle
	{
		SPoint points[3];

		STriangle() {}
		STriangle(const SPoint& p0, const SPoint& p1, const SPoint& p2)
		{
			points[0] = p0;
			points[1] = p1;
			points[2] = p2;
		}
	};

	std::vector<STriangle> m_triangles;

	struct SSphere
	{
		SPoint p;
		float r;

		SSphere() {}
		SSphere(const SPoint& p_, float r_) : p(p_), r(r_) {}
	};

	std::vector<SSphere> m_spheres;

	Vec3 m_eye, m_dir, m_up;

	static bool s_active;
	static bool s_hidden;
#endif
};

#endif NULL_RENDER_AUX_GEOM_H
